package procsim;

public class ALU implements Element{
    private MSignal op1, op2, result;
    private int numOfBits;
    private Signal cBit, add, sub, and, or, xor, asr, asl, inc, dec, trans;
    private Signal[] cSigs;
    public ALU(int num, MSignal in1, MSignal in2){
        numOfBits=num;
        op1=in1;
        if(in1!=null) in1.addElement(this);
        op2=in2;
        if(in2!=null) in2.addElement(this);
        result=new MSignal(numOfBits, 0);
    }
    private void updateCSigs(Signal cSig){
        cSigs=new Signal[] {add, sub, and, or, xor, asr, asl, inc, dec, trans};
        for(int i=0; i<cSigs.length; i++){
            if(cSigs[i]!=null && cSigs[i]!=cSig)
                cSigs[i].set(0);
        }
    }
    public ALU setAdd(Signal contr){
        if(contr!=null) contr.addElement(this);
        updateCSigs(add=contr);
        return this;
    }
    public ALU setSub(Signal contr){
        if(contr!=null) contr.addElement(this);
        updateCSigs(sub=contr);
        return this;
    }
    public ALU setAnd(Signal contr){
        if(contr!=null) contr.addElement(this);
        updateCSigs(and=contr);
        return this;
    }
    public ALU setOr(Signal contr){
        if(contr!=null) contr.addElement(this);
        updateCSigs(or=contr);
        return this;
    }
    public ALU setXor(Signal contr){
        if(contr!=null) contr.addElement(this);
        updateCSigs(xor=contr);
        return this;
    }
    public ALU setAsr(Signal contr){
        if(contr!=null) contr.addElement(this);
        updateCSigs(asr=contr);
        return this;
    }
    public ALU setAsl(Signal contr){
        if(contr!=null) contr.addElement(this);
        updateCSigs(asl=contr);
        return this;
    }
    public ALU setInc(Signal contr){
        if(contr!=null) contr.addElement(this);
        updateCSigs(inc=contr);
        return this;
    }
    public ALU setDec(Signal contr){
        if(contr!=null) contr.addElement(this);
        updateCSigs(dec=contr);
        return this;
    }
    public ALU setTrans(Signal contr){
        if(contr!=null) contr.addElement(this);
        updateCSigs(trans=contr);
        return this;
    }
    public ALU setResult(MSignal sig){
        result=sig;
        return this;
    }
    public ALU setCBit(Signal sig){
        cBit=sig;
        return this;
    }
    private void add(int[] arr1, int[] arr2){
        int res[] = new int[numOfBits];
        int carry = 0;
        for(int i=numOfBits-1; i>=0; i--){
            int tmp=arr1[i]+arr2[i]+carry;
            res[i]=tmp & 1; // vadimo najnizi bit
            carry=(tmp & 2)>>1; // vadimo drugi bit
        }
        if(cBit!=null) cBit.set(carry);
        if(result!=null) result.set(new MSignal(numOfBits, Util.binaryArray2Int(Util.reverseIntArray(res))));
    }
    private void subtract(int[] arr1, int[] arr2){
        int res[] = new int[numOfBits];
        int borrow = 0;
        for(int i=numOfBits-1; i>=0; i--){
            int tmp=arr1[i]-arr2[i]-borrow;
            res[i]=tmp & 1; // vadimo najnizi bit
            borrow=(tmp & 2)>>1; // vadimo drugi bit
        }
        if(cBit!=null) cBit.set(borrow);
        if(result!=null) result.set(new MSignal(numOfBits, Util.binaryArray2Int(Util.reverseIntArray(res))));
    }
    @Override
    public ALU tick() {
        int arr1[] = null, arr2[] = null;
        if(op1!=null) arr1 = Util.reverseIntArray(Util.signalArray2IntArray(op1.getBits()));
        if(op2!=null) arr2 = Util.reverseIntArray(Util.signalArray2IntArray(op2.getBits()));
        int res[] = new int[numOfBits];
        if (add!=null && !add.isNull()){
            add(arr1, arr2);
        }
        else if (sub!=null && !sub.isNull()){
            subtract(arr1, arr2);
        }
        else if (and!=null && !and.isNull()){
            for(int i=0; i<numOfBits; i++)
                res[i]=arr1[i]&arr2[i];
            if(result!=null) result.set(new MSignal(numOfBits, Util.binaryArray2Int(Util.reverseIntArray(res))));
        }
        else if (or!=null && !or.isNull()){
            for(int i=0; i<numOfBits; i++)
                res[i]=arr1[i]|arr2[i];
            if(result!=null) result.set(new MSignal(numOfBits, Util.binaryArray2Int(Util.reverseIntArray(res))));
        }
        else if (xor!=null && !xor.isNull()){
            for(int i=0; i<numOfBits; i++)
                res[i]=arr1[i]^arr2[i];
            if(result!=null) result.set(new MSignal(numOfBits, Util.binaryArray2Int(Util.reverseIntArray(res))));
        }
        else if (asr!=null && !asr.isNull()){
            for(int i=1; i<numOfBits; i++)
                res[i]=arr1[i-1];
            res[0]=arr1[0];
            if(result!=null) result.set(new MSignal(numOfBits, Util.binaryArray2Int(Util.reverseIntArray(res))));
        }
        else if (asl!=null && !asl.isNull()){
            for(int i=1; i<numOfBits; i++)
                res[i-1]=arr1[i];
            res[numOfBits-1]=0;
            if(result!=null) result.set(new MSignal(numOfBits, Util.binaryArray2Int(Util.reverseIntArray(res))));
        }
        else if (inc!=null && !inc.isNull()){
            arr2 = new int[numOfBits];
            arr2[numOfBits-1]=1;
            add(arr1, arr2);
        }
        else if (dec!=null && !dec.isNull()){
            arr2 = new int[numOfBits];
            arr2[numOfBits-1]=1;
            subtract(arr1, arr2);
        }
        else if (result!=null && trans!=null && !trans.isNull()){
            result.set(op1);
        }
        return this;
    }
    public Signal getCarry(){
        return cBit;
    }
    @Override
    public MSignal result() {
        return result;
    }
    @Override
    public String toString(){
        return "ALU:"+result;
    }
    @Override
    public void updateSignal(Sig sig) {
        cSigs=new Signal[] {add, sub, and, or, xor, asr, asl, inc, dec, trans};
        boolean changed = false;
        if(op1!=null && op1==sig){
            op1.set(sig.get(), false);
            changed = true;
        }
        if(op2!=null && op2==sig){
            op2.set(sig.get(), false);
            changed = true;
        }
        for(int i=0; i<cSigs.length; i++){
            if(cSigs[i]!=null && cSigs[i]==sig){
                cSigs[i].set(sig.get(), false);
                changed = true;
            }
        }
        if(changed) tick();
    }
}
